#include "head.h"

//=====静态注册方式=====================================================================
/**
 * extern "C" ：表示下边的代码采用C的编译方式
 * JNIEXPORT：JNI重要标记关键字，不能少(VS编译能通过，运行会报错) / (AS 运行不会报错)， 规则(标记为该方法可以被外部调用) Windows内部规则，与Linux内部规则 不同
 * jstring：代表 java 中的 String
 * JNICALL :  也是一个关键字，(可以少的) jni call (约束函数入栈顺序，和堆栈内存清理的规则)
 * jobject:  java传递下来的对象，就是本项目中 MainActivity 对象
 */
extern "C"
JNIEXPORT jstring JNICALL
//c++函数命名规则 Java_包名(以_连接，如果原包名中有下划线则表示1)_类名_方法名
Java_com_example_cxxdemo_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}
/**
 * 原生调c++方法，改变name的值
 */
extern "C"
JNIEXPORT void JNICALL
Java_com_example_cxxdemo_MainActivity_changeName(JNIEnv *env, jobject mainactivityObj) {
    //方式一：env->FindClass("全类名"）
    //方式二：env->GetObjectClass(jobject)
    jclass mainactivity_claz = env->FindClass("com/example/cxxdemo/MainActivity");
    jmethodID methodid_get_name = env->GetMethodID(mainactivity_claz, "getName",
                                                   "()Ljava/lang/String;");
//    //通过CallObjectMethod方式获取原生代码中name字段 getName()
    jstring get_name_str = static_cast<jstring>(env->CallObjectMethod(mainactivityObj,
                                                                      methodid_get_name));
    //GetStringUTFChars将Jni jstring 转为c/c++可识别的类型 char*
    const char *get_name_str_c = const_cast<char *>(env->GetStringUTFChars(get_name_str, NULL));
    LOGI("原生传递过来的参数 get_name_str :%s", get_name_str_c);
    //由于在栈空间操作，随时释放资源
    env->ReleaseStringUTFChars(get_name_str, get_name_str_c);
    env->DeleteLocalRef(get_name_str);

    // jfieldID GetFieldID(jclass clazz, const char* name, const char* sig);
    jfieldID name_field_id = env->GetFieldID(mainactivity_claz, "name", "Ljava/lang/String;");
    char *newvalue = "隔壁老王";
    jstring value = env->NewStringUTF(newvalue);
    //注意：Jni中jstring属于jobject
    env->SetObjectField(mainactivityObj, name_field_id, value);
    //释放资源
    env->DeleteLocalRef(value);
    env->DeleteLocalRef(mainactivity_claz);

}
/**
 * c++调原生add()
 */
extern "C"
JNIEXPORT jint JNICALL
Java_com_example_cxxdemo_MainActivity_callAddMethod(JNIEnv *env, jobject thiz) {
    //获取jclass
    jclass mainactivityclazz = env->GetObjectClass(thiz);
    jmethodID add_mid = env->GetMethodID(mainactivityclazz, "add", "(II)I");
    //通过CallIntMethod 调用原生代码中add(num1,num2)函数
    jint value = env->CallIntMethod(thiz, add_mid, 10010, 10086);
    LOGI("callAddMethod value : %d", value)
    env->DeleteLocalRef(mainactivityclazz);
    return value;
}
/**
 * c++原生数组处理
 */
extern "C"
JNIEXPORT void JNICALL
Java_com_example_cxxdemo_MainActivity_arrayTest(JNIEnv *env, jobject thiz, jint cout, jstring str,
                                                jintArray int_array, jobjectArray array) {
    LOGD("c++ 接收的 count: %d", cout)
    const char *str_c = env->GetStringUTFChars(str, NULL);
    LOGD("c++ 接收的 str: %s", str_c)
    //释放
    env->ReleaseStringUTFChars(str, str_c);
    int int_array_length = env->GetArrayLength(int_array);
    for (int i = 0; i < int_array_length; ++i) {
        //获取intarray数组的每个元素
        jint *int_array_item = env->GetIntArrayElements(int_array, NULL);
        LOGD("c++ 接收的 int_array item index: %d  value: %d", i, *(int_array_item + i))
        //在原值基础上加100
        *(int_array_item + i) = 100 + *(int_array_item + i);
        LOGD("c++ 更改后的 int_array item index: %d  value: %d", i, *(int_array_item + i))
        /**
         * 必须调用，此代码分2步：1、更改值；2、释放
         * mode: JNI_OK(0) 表示先刷新到JVM，再释放C++层数组
         *      JNI_COMMIT(1) 表示用操纵杆刷新到JVM
         *      JNI_ABORT(2) 释放C++层数组
         */
        env->ReleaseIntArrayElements(int_array, int_array_item, JNI_OK);
    }

    jint strlength = env->GetArrayLength(array);
    //遍历Array<String>数组
    for (int i = 0; i < strlength; ++i) {
        //修改前打印
        jobject str_item = env->GetObjectArrayElement(array, i);
        jstring item_str = static_cast<jstring>(str_item);
        const char *str_item_c = const_cast<char *>(env->GetStringUTFChars(item_str, NULL));
        LOGD("c++ 接收的 array item index: %d  value: %s", i, str_item_c)
        env->ReleaseStringUTFChars(item_str, str_item_c);

        //修改mainactivity中array的数据
        jstring new_string_item = env->NewStringUTF("二麻子");
        env->SetObjectArrayElement(array, i, new_string_item);

        env->DeleteLocalRef(new_string_item);
        env->DeleteLocalRef(str_item);
    }


}
/**
 * c++修改原生对象
 */
extern "C"
JNIEXPORT void JNICALL
Java_com_example_cxxdemo_MainActivity_putObject(
        JNIEnv *env, jobject thiz, jobject person,
        jstring str) {
    char *str_c = const_cast<char *>(env->GetStringUTFChars(str, NULL));

    jclass person_clazz = env->GetObjectClass(person);
    jmethodID mid_set_name = env->GetMethodID(person_clazz, "setName", "(Ljava/lang/String;)V");
    env->CallVoidMethod(person, mid_set_name, str);

    jmethodID mid_set_age = env->GetMethodID(person_clazz, "setAge", "(I)V");
    env->CallVoidMethod(person, mid_set_age, 28);

    env->ReleaseStringUTFChars(str, str_c);
    env->DeleteLocalRef(person_clazz);
}


/**
 * c++全局引用
 */
jclass personClazz = nullptr;

extern "C"
JNIEXPORT void JNICALL
Java_com_example_cxxdemo_MainActivity_testQuote(JNIEnv *env, jobject thiz) {
    //c/c++中 非0 即true
    if (!personClazz) {
        jclass tempClazz = env->FindClass("com/example/cxxdemo/Person");
        //提升为全局引用
        personClazz = static_cast<jclass>(env->NewGlobalRef(tempClazz));
        env->DeleteLocalRef(tempClazz);
    }
    //pserson初始化init函数， 可通过 Kotlin Bytecode 查看 Person.kt的字节码 public <init>(Ljava/lang/String;I)V
    jmethodID pinit = env->GetMethodID(personClazz, "<init>", "(Ljava/lang/String;I)V");
    jstring name = env->NewStringUTF("老李");
    //通过构造函数new Person("老李",100);
    jobject p1 = env->NewObject(personClazz, pinit, name, 100);

    //c++调用Person setName，setAge修改值
    jmethodID mid_set_name = env->GetMethodID(personClazz, "setName", "(Ljava/lang/String;)V");
    env->CallVoidMethod(p1, mid_set_name, name);
    jmethodID mid_set_age = env->GetMethodID(personClazz, "setAge", "(I)V");
    env->CallVoidMethod(p1, mid_set_age, 80);
    env->DeleteLocalRef(name);

    jmethodID tostring = env->GetMethodID(personClazz, "toString", "()Ljava/lang/String;");
    jstring person_str = static_cast<jstring>(env->CallObjectMethod(p1, tostring));
    char *person_str_c = const_cast<char *>(env->GetStringUTFChars(person_str, NULL));
    LOGI("c++创建的person ：%s", person_str_c)
    env->ReleaseStringUTFChars(person_str, person_str_c);

    env->DeleteLocalRef(p1);
}

/**
 * 释放全局引用
 */
extern "C"
JNIEXPORT void JNICALL
Java_com_example_cxxdemo_MainActivity_releaseQuote(JNIEnv *env, jobject thiz) {
    if (personClazz) {
        env->DeleteGlobalRef(personClazz);
        //释放后必须将指针指向空指针，否则会出现悬空指针问题
        personClazz = nullptr;
        LOGI("手动释放全局引用成功success.")
    }

}

//************************以下代码是动态注册************************************/
/**
 * 动态注册步骤：
 * 1、定义 JavaVM
 * 2、注册JNINativeMethod[]
 * 3、重写JNI_Onload()函数
 * 4、调用JNIEnv RegisterNatives()函数
 */

JavaVM *jvm = nullptr;//若不赋值null，则会指向系统指针，类似于0x03234乱码

const char *mainactivity_class_name = "com/example/cxxdemo/MainActivity2";

/**
 * native调用的具体函数
 * 等价于 静态注册 Java_com_example_cxxdemo_MainActivity2_dynamicJavaMethodType1(JNIEnv *env, jobject thiz)
 */
void dynamicaAtion1() {
    LOGI("我是动态注册函数 1 dynamicJavaMethodType1")
}

/**
 * kt中返回类型为Int值，所以此时应返回 jint *
 * @return
 */
jint *dynamicaAtion2(JNIEnv *env, jobject thiz, jstring str) {
    const char *str_c = env->GetStringUTFChars(str, NULL);
    LOGI("c+= 我是动态注册事件2  dynamicaAtion2 接收到的数据：%s", str_c)
    env->ReleaseStringUTFChars(str, str_c);
    return reinterpret_cast<jint *>(666);
}

//=======JNI线程========================================

class Context {
public:
    //JavaVM* 是进程级的，相当于app进程的一个成员变量
    //JNIEnv* 不能跨线程操作
    jobject instance = nullptr;//不能跨线程，不能垮函数
};

/**
 * 相当于 java 子线程 run函数
 * 结构是： 返回类型   函数指针别名  参数
 * @param args
 * @return
 */
void *cpp_thread_run(void *args) {
    LOGI("c++  thread 异步线程 run")
    Context *context = static_cast<Context *>(args);
    //调activity中的showUI()函数
    //缺少env,缺少class
    JNIEnv *env;
    //绑定，获得env环境。注意此处第一个参数为JNIEnv** p_env
    jint result = ::jvm->AttachCurrentThread(&env, nullptr);
    if (result) {
        return nullptr;
    }
    jclass clazz = env->GetObjectClass(context->instance);
    jmethodID mid_showui = env->GetMethodID(clazz, "showUI", "()V");
    env->CallVoidMethod(context->instance, mid_showui);
    //解除绑定的当前的env环境
    ::jvm->DetachCurrentThread();

    return nullptr;
}


/**
 * c++线程
 * @param env
 * @param thiz
 */
void nativeCThread(JNIEnv *env, jobject thiz){
    LOGI("c++  nativeCThread 异步线程 ")
    //当前属于主线程中，主线程点击按钮，调native函数，执行到此函数
    Context * context = new Context;
    context->instance = env->NewGlobalRef(thiz);
    pthread_t pid;
    /**
     * pthread_create(pthread_t* __pthread_ptr, pthread_attr_t const* __attr, void* (*__start_routine)(void*), void*);
     * 参数pthread_t* __pthread_ptr:线程标记ID
     * 参数二：pthread配置的参数集
     * 参数三：函数指针
     *  参数四：void * args
     */
    pthread_create(&pid, nullptr,cpp_thread_run,context);
    //不执行此函数 会报错JNI DETECTED ERROR IN APPLICATION: use of invalid jobject 0x1c
    //pthread_join 和java的join类似，cpp_thread_run执行完之后才会执行下边代码
    pthread_join(pid, nullptr);
    //线程用完需要回收
    env->DeleteGlobalRef(context->instance);
    delete(context);
    context = nullptr;
}

//=======JNI线程========================================
/**
 * typedef struct {
    const char* name;
    const char* signature;
    void*       fnPtr;
    } JNINativeMethod;
*可以看到JNINativeMethod参数有 3个，分别代表
*name: 和java一一对应的函数名
* signature： 签名
*  fnPtr ：方法指针
 */
static const JNINativeMethod jniNativeMethod[] = {
        {"dynamicJavaMethod01", "()V",                   (void *) dynamicaAtion1},
        {"dynamicJavaMethod02", "(Ljava/lang/String;)I", (jint *) dynamicaAtion2},
        {"nativeThread", "()V", (void *) nativeCThread}
};


/**
 * JNI_Onload类似于java的构造函数；
 * 此时重写后可自行处理自己的逻辑
 */
jint JNI_OnLoad(JavaVM *javaVm, void *) {
    ::jvm = javaVm;
    //注册，
    JNIEnv *env = nullptr;
    jint result = javaVm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6);
    //result ==0 :成功
    if (result != JNI_OK) {
        LOGD("动态注册GetEnv  fail")
        return -1;
    }
    LOGI("System.loadLibrary ---》 JNI Loading init...")
    jclass mainactivity_clazz = env->FindClass(mainactivity_class_name);
    //参数分别是： class ，定义的JNINativeMethod, methods数组大小
    jint regist_result = env->RegisterNatives(mainactivity_clazz, jniNativeMethod,
                                              sizeof jniNativeMethod / sizeof(JNINativeMethod));
    if (regist_result) { // 非零true 进if
        LOGD("动态注册 fail")
    } else {
        LOGI("动态注册 success")
    }
    return JNI_VERSION_1_6;
}
